﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Syk.Library.Scheduling;
using Syk.Library.Common;

namespace Syk.Library.Cache
{
    public class SchedulerCacheItem<TObject> : ISchedulerItem
    {
        public TObject Value { get; set; }
        public object Key { get; set; }

        #region ISchedulerItem Members

        public DateTime StartTime { get; set; }

        #endregion
    }

    public class MemoryCacheProvider : ICacheProvider, IDisposable
    {
        #region fields

        private readonly Dictionary<object, CacheObject> m_Dic = new Dictionary<object, CacheObject>();
        private Scheduler<SchedulerCacheItem<CacheObject>> m_Scheduler;
        private readonly object m_SyncRoot = new object();

        #endregion

        #region ctor

        public MemoryCacheProvider()
        {
            AllowActiveClearItemsScheduler = false;
        }

        #endregion

        #region scheduler
        private bool m_AllowActiveClearItemsScheduler;
        public bool AllowActiveClearItemsScheduler
        {
            get
            {
                return m_AllowActiveClearItemsScheduler;
            }
            set
            {
                if (m_AllowActiveClearItemsScheduler != value)
                {
                    m_AllowActiveClearItemsScheduler = value;
                    if (!m_AllowActiveClearItemsScheduler)
                    {
                        UnSubscribeToSchedulerItemExecution();
                        m_Scheduler.Clear();
                        m_Scheduler.Dispose();
                        m_Scheduler = null;

                    }
                    else
                    {
                        m_Scheduler = new Scheduler<SchedulerCacheItem<CacheObject>>(new DefaultSchedulingConfiguration(), new LocalDateTimePreciseService());
                        SubscribeToSchedulerItemExecution();
                    }

                    OnActiveClearItemsSchedulerChanged(this, EventArgs.Empty);
                }
            }
        }
        public event EventHandler OnActiveClearItemsSchedulerChanged = delegate { };
        private void SubscribeToSchedulerItemExecution()
        {
            if (m_Scheduler != null)
            {
                m_Scheduler.SchedulerItemExecution += OnSchedulerItemExecution;
            }
        }
        private void UnSubscribeToSchedulerItemExecution()
        {
            if (m_Scheduler != null)
            {
                m_Scheduler.SchedulerItemExecution -= OnSchedulerItemExecution;
            }
        }

        protected virtual void OnSchedulerItemExecution(object sender, EventArgs<SchedulerCacheItem<CacheObject>> e)
        {
            lock (m_SyncRoot)
            {
                m_Dic.Remove(e.Object.Key);
            }
        }
        #endregion

        #region ICacheModule Members

        public object Get(object uid, TimeSpan timeout)
        {
            lock (m_SyncRoot)
            {
                if (!m_Dic.ContainsKey(uid))
                    return null;

                CacheObject obj = m_Dic[uid];
                if (!AllowActiveClearItemsScheduler && obj.IsExpired(timeout))
                {
                    m_Dic.Remove(uid);
                    return null;
                }
                else
                {
                    return obj.Value;
                }
            }
        }

        public object Get(object uid)
        {
            lock (m_SyncRoot)
            {
                if (m_Dic.ContainsKey(uid))
                {
                    return m_Dic[uid].Value;
                }
                else
                {
                    return null;
                }
            }
        }

        public void Set(object uid, object value)
        {
            lock (m_SyncRoot)
            {
                m_Dic[uid] = new CacheObject(DateTime.Now, value);
            }
        }

        public void Set(object uid, object value, TimeSpan timeout)
        {
            lock (m_SyncRoot)
            {
                if (AllowActiveClearItemsScheduler)
                {
                    SchedulerCacheItem<CacheObject> schedulerCacheItem = new SchedulerCacheItem<CacheObject>
                    {
                        StartTime = DateTime.Now.Add(timeout),
                        Key = uid
                    };
                    m_Scheduler.Add(schedulerCacheItem);
                }
                m_Dic[uid] = new CacheObject(DateTime.Now, value);
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (AllowActiveClearItemsScheduler)
            {
                UnSubscribeToSchedulerItemExecution();
                m_Scheduler.Dispose();
            }
        }

        #endregion
    }
}
